package com.dlyk.config.listener;

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.excel.util.ListUtils;
import com.dlyk.mapper.ClueMapper;
import com.dlyk.model.Clue;
import com.dlyk.model.User;
import com.dlyk.util.JSONUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.context.SecurityContextHolder;

import java.util.Date;
import java.util.List;

/**
 * 上传数据监听器（文件读取监听器）
 *
 *  每读一行Excel的数据，就会触发该监听器中的invoke()方法
 *  所有数据读完之后会调用doAfterAllAnalysed()方法
 */
@Slf4j
public class UploadDataListener implements ReadListener<Clue> {

    /**
     * 每隔5条存储数据库，实际使用中可以100条，然后清理list，方便内存回收
     */
    private static final int BATCH_COUNT = 5;

    /**
     * 缓存List（指定大小的List，不能无限扩容）
     */
    private List<Clue> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);

    /**
     * 假设这个是一个DAO，当然有业务逻辑这个也可以是一个service，当然如果不用存储数据这个对象没用
     */
    private ClueMapper clueMapper;

    /**
     * 这里是demo，所以随便new一个。实际使用如果到了spring，请使用下面的有参构造函数
     */
    public UploadDataListener() {
        // clueMapper = new ClueMapper();
    }

    /**
     * 如果使用了spring，请使用这个构造方法。每次创建Listener的时候需要把spring管理的类传进来
     */
    public UploadDataListener(ClueMapper clueMapper) {
        this.clueMapper = clueMapper;
    }

    /**
     * 每解析一条数据都会来调用
     */
    @Override
    public void invoke(Clue clue, AnalysisContext context) {

        log.info("解析到一条数据:{}", JSONUtils.toJSON(clue));

        // 通过安全上下文持有器获取上下文，通过上下文获取认证信息，通过认证信息获取当前登录的用户
        User loginUser = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        // 给当前解析的线索设置创建时间和创建者
        clue.setCreateTime(new Date());
        clue.setCreateBy(loginUser.getId());

        // 每解析一行数据，就把该数据放到缓存List当中
        cachedDataList.add(clue);

        // 达到BATCH_COUNT了，需要去存储一次数据库，防止数据几万条数据在内存，容易OOM
        if (cachedDataList.size() >= BATCH_COUNT) {

            // 将缓存List中的数据写入到数据库
            saveData();

            // 存储完成清理 list
            cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
        }
    }

    /**
     * 所有数据解析完成后调用
     */
    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {

        // 这里也要保存数据，确保最后遗留的数据也存储到数据库
        saveData();

        log.info("所有数据解析完成！");
    }

    /**
     * 将数据存储到数据库
     */
    private void saveData() {

        log.info("{}条数据，开始存储数据库！", cachedDataList.size());

        // 调用mapper将数据持久化到数据库
        clueMapper.insertClueList(cachedDataList);

        log.info("存储数据库成功！");
    }

}

